package ua.org.extjsbuilder.parser.runtime.builder;

import java.io.EOFException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import ua.org.extjsbuilder.parser.IInstruction;
import ua.org.extjsbuilder.parser.JSObject;
import ua.org.extjsbuilder.parser.runtime.UnparsedToken;
import ua.org.extjsbuilder.parser.runtime.instructions.Condition;
import ua.org.extjsbuilder.parser.runtime.instructions.TryCatchFinally;
import ua.org.extjsbuilder.parser.scanner.CharPattern;

abstract class ConditionsInterpreter extends KeywordsInterpreter {
	private static final String ELSE_KEYWORD = "else";
	private static final String DEFAULT_KEYWORD = "default";
	private static final String CASE_KEYWORD = "case";
	private static final String CATCH_KEYWORD = "catch";
	private static final String FINALLY_KEYWORD = "finally";
	
	protected IInstruction condition;
	protected List<IInstruction> mainBlock;
	private List<IInstruction> elseBlock;
	private Map<JSObject,List<IInstruction>> cases;
	private JSObject currentCase;

	ConditionsInterpreter(UnparsedToken start) {
		super(start);
	}

	@Override
	boolean next() throws EOFException {
		super.next();
		try {
			switch(state){
			case TRY_STATEMENT:
				parseTryCatchFinally();
				break;
			case IF_STATEMENT:
				parseCondition();
				parseMainBlock();
				parseElseBlock();
				createIfStatement();
				break;
			case SWITCH_STATEMENT:
				parseCondition();
				parseBody();
				createSwitchStatement();
				break;
			case CASE_STATEMENT:
			case DEFAULT_STATEMENT:
				err("This keyword can be used in switch block only");
				break;
			case CATCH_STATEMENT:
			case FINALLY_STATEMENT:
				err("This keyword can be used after try block only");
				break;
			}
		}
		catch(EOFException ex){
			err("Unterminated condition block");
			throw ex;
		}
		return true;
	}

	private void createSwitchStatement() {
		lastComplete = new Condition(condition,cases);
		state = LEFT_START;
	}

	private void parseBody() throws EOFException {
		cases = new HashMap<JSObject, List<IInstruction>>();
		skipWhiteSpaces();
		if(is('{'))pos++;
		else err("{ expected");
		skipWhiteSpaces();
		GlobalInterpreter interpreter = new GlobalInterpreter(token,pos);
		interpreter.setHandler(getHandler());
		while(interpreter.data[interpreter.pos]!='}'){
			parseCaseOrDefault(interpreter);
			if(cases.get(currentCase)==null){
				cases.put(currentCase, new LinkedList<IInstruction>());
			}
			else{
				interpreter.pos--;
			}
			while(interpreter.next()&&!interpreter.isComplete());
			cases.get(currentCase).add(interpreter.getLastComplete());
			interpreter.skipWhiteSpaces();
		}
		setState(interpreter);
	}

	private void parseCaseOrDefault(GlobalInterpreter interpreter) throws EOFException {
		int p = interpreter.pos;
		char[] data = interpreter.data;
		
		if(data[p]==CASE_KEYWORD.charAt(0)){
			int x=0;
			for(;x<CASE_KEYWORD.length();x++){
				if((p+x)>=data.length||
					CASE_KEYWORD.charAt(x)!=data[p+x]){
					return;
				}
			}
			p+=x;
			if(p<data.length&&
				!CharPattern.ALLOW_WHITE_SPACES.match(data[p])){
				return;
			}
			interpreter.pos = p;
			interpreter.skipWhiteSpaces();
			currentCase = interpreter.getLiteral();
			if(interpreter.data==null)interpreter.nextToken(true);
			expectColon(interpreter);
		}
		else if(data[p]==DEFAULT_KEYWORD.charAt(0)){
			int x=0;
			for(;x<DEFAULT_KEYWORD.length();x++){
				if((p+x)>=data.length||
					DEFAULT_KEYWORD.charAt(x)!=data[p+x]){
					return;
				}
			}
			interpreter.pos = p+x;
			currentCase = null;
			expectColon(interpreter);
		}
		else {
			return;
		}
		interpreter.pos++;
		interpreter.skipWhiteSpaces();
		interpreter.pos--;
		setState(interpreter);
	}

	private void expectColon(GlobalInterpreter interpreter) throws EOFException {
		if(interpreter.pos<interpreter.data.length||
			CharPattern.ALLOW_WHITE_SPACES.match(
					interpreter.data[interpreter.pos])){
			interpreter.skipWhiteSpaces();
		}
		if(interpreter.data[interpreter.pos]!=':'){
			interpreter.err("Colon expected");
		}
	}

	private void createIfStatement() {
		lastComplete = new Condition(condition,
				mainBlock,elseBlock);
		state = LEFT_START;
	}

	private void parseElseBlock() throws EOFException {
		GlobalInterpreter interpreter = new GlobalInterpreter(token,pos+1);
		try{
			interpreter.skipWhiteSpaces();
		}
		catch(EOFException ex){
			return;
		}
		int i=0;
		elseBlock = null;
		char[] kw = ELSE_KEYWORD.toCharArray();
		while(i<kw.length&&interpreter.isLine()){
			if(interpreter.incr()!=kw[i++])return;
		}
		interpreter.skipWhiteSpaces();
		
		if(interpreter.is('{')){
			elseBlock = parseBlock(interpreter);
		}
		else {
			elseBlock = new LinkedList<IInstruction>();
			interpreter.pos--;
			while(interpreter.next()&&!interpreter.isComplete());
			elseBlock.add(interpreter.getLastComplete());
			setState(interpreter);
		}
	}

	protected void parseMainBlock() throws EOFException {
		GlobalInterpreter interpreter = new GlobalInterpreter(token,pos);
		interpreter.skipWhiteSpaces();
		mainBlock = null;
		
		if(interpreter.is('{')){
			mainBlock = parseBlock(interpreter);
		}
		else {
			interpreter.pos--;
			mainBlock = new LinkedList<IInstruction>();
			while(interpreter.next()&&!interpreter.isComplete());
			mainBlock.add(interpreter.getLastComplete());
			setState(interpreter);
		}
	}	

	protected void parseCondition() throws EOFException {
		skipWhiteSpaces();
		if(data[pos]!='('){
			err("Open bracket expected");
		}
		pos++;
		skipWhiteSpaces();
		pos--;
		ExpressionsInterpreter intr = new ExpressionsInterpreter(
				token,pos,CharPattern.compile(")"));
		condition = intr.buildExpression();
		setState(intr);
		skipWhiteSpaces();
		if(data[pos]!=')'){
			err("Closing bracket expected");
		}
		else {
			pos++;
			skipWhiteSpaces();
		}
	}

	private void parseTryCatchFinally() throws EOFException {
		GlobalInterpreter interpreter = new GlobalInterpreter(token,pos);
		interpreter.skipWhiteSpaces();
		if(!interpreter.is('{'))err("Open bracket expected");
		List<IInstruction> tryBlock = parseBlock(interpreter);
		List<IInstruction> catchBlock = null;
		String errorName = null;
		List<IInstruction> finallyBlock = null;
		
		interpreter.skipWhiteSpaces();
		if(!interpreter.is('}'))err("Closing bracket expected");
		interpreter.pos++;
		interpreter.skipWhiteSpaces();
		if(interpreter.isKeyword(CATCH_KEYWORD)){
			interpreter.skip(CATCH_KEYWORD.length());
			
			interpreter.skipWhiteSpaces();
			
			if(!interpreter.is('('))err("Open bracket expected");
			interpreter.pos++;
			interpreter.skipWhiteSpaces();
			
			StringBuilder builder = new StringBuilder();
			char c = interpreter.chr();
			while(!CharPattern.ALLOW_WHITE_SPACES.match(c)&&c != ')') {
				builder.append(c);
				interpreter.pos++;
				if(!interpreter.isLine())break;
				c = interpreter.chr();
			}
			errorName = builder.toString();
			interpreter.skipWhiteSpaces();
			if(!interpreter.is(')'))err("Closing bracket expected");
			interpreter.pos++;
			interpreter.skipWhiteSpaces();
			if(!interpreter.is('{'))err("Open bracket expected");
			catchBlock = parseBlock(interpreter);
			if(!interpreter.is('}'))err("Closing bracket expected");
			interpreter.pos++;
			interpreter.skipWhiteSpacesNoError();
		}
		if(interpreter.isKeyword(FINALLY_KEYWORD)){
			interpreter.skip(FINALLY_KEYWORD.length());
			interpreter.skipWhiteSpaces();
			if(!interpreter.is('{'))err("Open bracket expected");
			finallyBlock = parseBlock(interpreter);
			interpreter.skipWhiteSpaces();
			if(!interpreter.is('}'))err("Closing bracket expected");
			interpreter.pos++;
			interpreter.skipWhiteSpacesNoError();
		}
		setState(interpreter);
		
		if(interpreter.isKeyword(CATCH_KEYWORD)){
			err("catch keyword should preseed finally");
		}
		if(catchBlock==null&&finallyBlock==null){
			err("catch or finally expected");
		}
		lastComplete = new TryCatchFinally(tryBlock,
				catchBlock,errorName,finallyBlock);
		state = LEFT_START;
	}
}
